/**
 * 
 */
package org.ranch.web.controllers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.ranch.api.ConceptCategoryAnnotation;
import org.ranch.api.DefaultConceptCategories;
import org.ranch.api.security.PermissionConstants;
import org.ranch.api.service.ConceptService;
import org.ranch.api.service.PersonService;
import org.ranch.api.service.UserService;
import org.ranch.api.utils.RanchUtil;
import org.ranch.model.ConceptCategory;
import org.ranch.model.Person;
import org.ranch.model.Role;
import org.ranch.model.UserStatus;
import org.ranch.model.exception.RanchValidationException;
import org.ranch.web.WebConstants;
import org.ranch.web.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

/**
 * @author Jmpango
 * 
 */
@Controller
@RequestMapping("user")
public class UserControlller {

    @Autowired
    private ConceptService conceptService;

    @Autowired
    private PersonService personService;

    @Autowired
    private UserService userService;

    private Logger log = LoggerFactory.getLogger(UserControlller.class);

    /**
     * web data binder
     * 
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
    }

    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=view", "item=users" })
    public ModelAndView viewUsersHandler(
	    @RequestParam(value = "pageNo", required = false) Integer pageNo, ModelMap model) {

	List<Person> persons = new ArrayList<Person>();
	persons = personService.getPerson(pageNo);

	model.put("userList", persons);

	WebUtils.prepareNavigation("User", personService.getTotalNumberOfPerson(), persons.size(),
		((pageNo == null) ? 1 : pageNo), "menu?action=view&item=users", model);

	return new ModelAndView("addUser", model);
    }

    /**
     * handles the request for adding a new user
     * 
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=add" })
    public ModelAndView addUserHandler(ModelMap model) {
	prepareUserFormModel(model);
	model.put("person", new Person());
	return new ModelAndView("newUser", model);
    }

    /**
     * @param model
     */
    private void prepareUserFormModel(ModelMap model) {
	model.put("gender", RanchUtil.getGenderList());
	List<Role> roles = userService.getRoles();
	model.put("roles", roles);
	model.put("userstatus", new UserStatus[] { UserStatus.ENABLED,
			UserStatus.DISABLED });
	try {
	    ConceptCategoryAnnotation responsibilityCateoryAnnotation = RanchUtil
		    .getConceptCategoryFieldAnnotation(DefaultConceptCategories.class,
			    DefaultConceptCategories.RESPONSIBILITY_TYPE);

	    if (responsibilityCateoryAnnotation != null) {
		ConceptCategory concept = conceptService
			.getConceptCategoryById(responsibilityCateoryAnnotation.id());
		if (concept != null) {
		    model.put("responsibility", conceptService.getConceptsByCategory(concept));
		}
	    }
	} catch (Exception e) {
	    log.debug(e.getMessage().getClass().getName(), e);
	}

    }

    /**
     * Save a new user into the system
     * 
     * @param photoFile
     * @param personModel
     * @param result
     * @param model
     * @param request
     * @return
     */
    
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.POST, params = { "action=save" })
    public ModelAndView saveUserHandler(
	    @RequestParam(value = "file", required = true) MultipartFile photoFile,
	    @ModelAttribute("person") Person personModel, BindingResult result,
	    ModelMap model, HttpServletRequest request) {

	    Person person = personModel;
	    try {   
	    personService.validatePerson(person);
	    
	    validateUserImages(photoFile);
	    
	    if (StringUtils.isNotBlank(personModel.getId())) {
		person = personService.getPersonById(personModel.getId());
		copyUserMember(personModel, person);
	    }

	    if (photoFile != null && photoFile.getSize() > 0) {
		try {
		    person.setProfilePicture(photoFile.getBytes());
		} catch (IOException e) {
		    e.printStackTrace();
		}
	    }
		personService.savePerson(person);
	    
	    model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
		    "User saved sucessfully!!!");
	    return viewUsersHandler(null, model);
	} catch (RanchValidationException e) {
		model.put(WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE, e.getMessage());
		prepareUserFormModel(model);
		model.put("person", person);
		return new ModelAndView("newUser", model);
	    }
    }

    /**
     * @param personModel
     * @param person
     */
    private void copyUserMember(Person fromPerson, Person toPerson) {
	toPerson.setAddresses(fromPerson.getAddresses());
	toPerson.setFirstName(fromPerson.getFirstName());
	toPerson.setLastName(fromPerson.getLastName());
	toPerson.setGender(fromPerson.getGender());
	toPerson.setOtherInformation(fromPerson.getOtherInformation());
	toPerson.setNextOfKins(fromPerson.getNextOfKins());
	toPerson.setNationality(fromPerson.getNationality());
	toPerson.setDateOfBirth(fromPerson.getDateOfBirth());
	toPerson.setResponsibility(fromPerson.getResponsibility());
	toPerson.setUsername(fromPerson.getUsername());
	toPerson.setSecretQuestion(fromPerson.getSecretQuestion());
	toPerson.setSecretAnswer(fromPerson.getSecretAnswer());
	toPerson.setStatus(fromPerson.getStatus());
	toPerson.setClearTextPassword(fromPerson.getClearTextPassword());
	toPerson.setRoles(fromPerson.getRoles());
    }

    /**
     * @param photoFile
     * @throws RanchValidationException
     */
    private void validateUserImages(MultipartFile photoFile) throws RanchValidationException {
	if (!WebUtils.isFileAnImage(photoFile))
	    throw new RanchValidationException(
		    "the file uploaded for the user photo is not an image.");

	if (!WebUtils.isValidSize(photoFile, WebConstants.DEFAULT_IMAGE_SIZE_IN_BYTES)) {
	    throw new RanchValidationException(String.format(
		    "the user photo exceeds the required size of >> %s bytes",
		    WebConstants.DEFAULT_IMAGE_SIZE_IN_BYTES));

	}

    }

    @RequestMapping(method = RequestMethod.GET, params = { "item=pic" })
    public void personPictureHandler(@RequestParam("personid") String personId,
	    HttpServletResponse response) throws Exception {

	Person person = personService.getPersonById(personId);

	if (person != null) {
	    if (person.getProfilePicture() != null && person.getProfilePicture().length > 0) {

		String contentType = "image/jpeg";
		writePictureOnResponse(contentType, person.getProfilePicture(), response);
	    }
	}
    }

    /**
     * writes the given bytes for a picture on the given servlet response.
     * 
     * @param contentType
     *            the content type of the image e.g. image/jpeg
     * @param pic
     * @param response
     * @throws IOException
     */
    private void writePictureOnResponse(String contentType, byte[] pic, HttpServletResponse response)
	    throws IOException {
	try {
	    response.setContentType(contentType);
	    response.getOutputStream().write(pic);
	} catch (IOException e) {
	    log.error(UserControlller.class.getName(), e);
	} finally {
	    response.getOutputStream().flush();
	}
    }

    /**
     * handles the request for deleting users whose ids are in the given list
     * 
     * @param userIds
     * @param response
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=delete",
	    "item=user" })
    public ModelAndView deleteStaffMember(
	    @RequestParam("userIdsToDelete") String userIdsToDelete,
	    @RequestParam("pageNo") int pageNo, ModelMap model) {

	List<String> userIds = convertStringToList(userIdsToDelete, ",");

	log.info("IDs of staff members to be deleted :" + userIdsToDelete);
	try {

	    for (String id : userIds) {
		Person perosn = personService.findPersonById(id);
		if (perosn != null) {
		    personService.deletePerson(perosn);
		}
	    }

	    model.put(WebConstants.MODEL_ATTRIBUTE_SYSTEM_MESSAGE,
		    "Users(s) deleted succesfully");
	} catch (Exception ex) {
	    model.put(WebConstants.MODEL_ATTRIBUTE_ERROR_MESSAGE, "Error: "
		    + ex.getMessage());
	    log.error(null, ex);
	}

	return viewUsersHandler(pageNo, model);
    }

    /**
     * takes a String "long string" and a separator, splits the string and
     * returns the parts in a string List
     * 
     * @param longString
     * @param separator
     */
    public static List<String> convertStringToList(String longString, String separator) {
	String[] stringArray;
	if (separator == null)
	    separator = ",";
	if (separator == "")
	    separator = ",";
	stringArray = longString.split(separator);

	List<String> list = new ArrayList<String>();
	for (int i = 0; i < stringArray.length; i++)
	    list.add(i, stringArray[i]);

	return list;
    }
    
    /**
     * Handles editing a new user.
     * 
     * @param id
     * @param model
     * @return
     */
    @Secured(PermissionConstants.PERM_ADMIN)
    @RequestMapping(method = RequestMethod.GET, params = { "action=edit" })
    public ModelAndView editUserHandler(@RequestParam("id") String id, ModelMap model) {
	
	Person person = personService.getPersonById(id);
	if(person != null){
	    prepareUserFormModel(model);
		model.put("person", person);
		return new ModelAndView("newUser", model);
	}
	return null;
    }
}
